﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JeuDeDames_v1._02
{
    public class Ordinateur : Joueur
    {
        #region Propriétés
        public int profondeur = 2;  
        #endregion

        #region Constructeurs
        public Ordinateur(int color)
        {
            couleur = color;
            this.lesPieces = new List<Piece>();
            this.piecesMangees = new List<int>();
            this.nbDames = 0;
            if (color == BLANC)
            {
                this.nom = "IA Blanc";
                this.monTour = true;
                indicePromote = new int[5] { 1, 3, 5, 7, 9 };
            }
            else if (color == NOIR)
            {
                this.nom = "IA Noir";
                this.monTour = false;
                indicePromote = new int[5] { 90, 92, 94, 96, 98 };
            }
        }

        public Ordinateur(int color, int profondeur, string nom)
        {
            couleur = color;
            this.profondeur = profondeur;
            this.nom = nom;
            this.lesPieces = new List<Piece>();
            this.piecesMangees = new List<int>();
            this.nbDames = 0;
            if (color == BLANC)
            {
                this.monTour = true;
                indicePromote = new int[5] { 1, 3, 5, 7, 9 };
            }
            else if (color == NOIR)
            {
                this.monTour = false;
                indicePromote = new int[5] { 90, 92, 94, 96, 98 };
            }
        } 
        #endregion

        #region Méthodes
        public int evaluation(Damier d, Joueur j)
        {
            
            //if (j.couleur == BLANC)
            //{
            //    Console.WriteLine("BLANC : " + (d.blanc.lesPieces.Count() - d.noir.lesPieces.Count()));
            //    return (d.blanc.lesPieces.Count() - d.noir.lesPieces.Count());   //différence de pions entre le joueur et son adversaire + 20.
            //}
            //else
            //{
            //    Console.WriteLine("NOIR : " + (d.noir.lesPieces.Count() - d.blanc.lesPieces.Count()));
            //    return (d.noir.lesPieces.Count() - d.blanc.lesPieces.Count());   //différence de pions entre le joueur et son adversaire + 20.
            //}

            Joueur adverse = null;
            if (d.blanc == j)
            {
                adverse = d.noir;
                if (d.finPartie() == -1)
                    return 32000 - d.nbTour;
                if (d.finPartie() == 1)
                    return -32000 + d.nbTour;
            }
            else if (d.noir == j)
            {
                adverse = d.blanc;
                if (d.finPartie() == 1)
                    return 32000 - d.nbTour;
                if (d.finPartie() == -1)
                    return -32000 + d.nbTour;
            }

            else
            {
                int valeurPieceIA = 0;
                int valeurPieceHU = 0;
                foreach (Piece piece in j.lesPieces)
                {
                    if (piece is Pion)
                        valeurPieceIA += 2;
                    if (piece is Dame)
                        valeurPieceIA += 6;
                }
                foreach (Piece piece in adverse.lesPieces)
                {
                    if (piece is Pion)
                        valeurPieceHU += 2;
                    if (piece is Dame)
                        valeurPieceHU += 6;
                }
                adverse.calculCoupsPossibles(d);
                foreach (Coup unCoup in adverse.coupsPossibles)
                {
                    valeurPieceHU += 1;
                }
                return Convert.ToInt16(valeurPieceIA - valeurPieceHU);
            }
         }

        public Coup minMaxPremier(Damier d)  // min correspond au minimum, max correspond au maximum.
        {
            if (coupsPossibles.Count() == 1)    //Si l'ordinateur a qu'un seul coup à jouer.
                return coupsPossibles[0];

            Coup meilleurCoup = null;
            Joueur adv = null;
            if (this == d.blanc)
                adv = d.noir;
            else if (this == d.noir)
                adv = d.blanc;
            int max = int.MinValue;
            int eval;
            Coup reference = null;
            List<Coup> coupsJouables = new List<Coup>();
            foreach (Coup c in this.coupsPossibles)
            {
                reference = c;
                this.jouer(d, ref reference);
                this.viderPiecesMangees(d);
                d.afficheDamier();
                d.changerTour();    //change monTour du joueur et calcul ses coups possibles
                eval = MIN(d, adv, profondeur - 1);
                if (eval > max)
                {
                    max = eval;
                    coupsJouables.Clear();
                    coupsJouables.Add(c);
                }
                else if (eval == max)
                {
                    coupsJouables.Add(c);
                }
                d.annulerUnTour();
                d.afficheDamier();
            }
            Random r = new Random();
            meilleurCoup = coupsJouables[r.Next(coupsJouables.Count())];
            return meilleurCoup;
        }

        private int MIN(Damier d, Joueur adv, int prof)
        {
            Joueur j = null;
            if (adv == d.blanc)
                j = d.noir;
            else if (adv == d.noir)
                j = d.blanc;

            if (prof == 0 || d.finPartie() != 0)
                return evaluation(d, j);
            else
            {
                int min = int.MaxValue;
                int eval;
                Coup reference;
                foreach (Coup c in adv.coupsPossibles)
                {
                    reference = c;
                    if (adv is Humain)
                        (adv as Humain).jouerIA(d, ref reference);
                    else
                        adv.jouer(d, ref reference);
                    adv.viderPiecesMangees(d);
                    d.afficheDamier();
                    d.changerTour();    //change monTour du joueur et calcul ses coups possibles
                    eval = MAX(d, j, prof - 1);
                    d.annulerUnTour();
                    if (eval < min)
                    {
                        min = eval;
                    }
                }
                return min;
            }
        }

        private int MAX(Damier d, Joueur j, int prof)
        {
            Joueur adv = null;
            if (j == d.blanc)
                adv = d.noir;
            else if (j == d.noir)
                adv = d.blanc;

            if (prof == 0 || d.finPartie() != 0)
                return evaluation(d, j);
            else
            {
                int max = int.MinValue;
                int eval;
                Coup reference;
                foreach (Coup c in j.coupsPossibles)
                {
                    reference = c;
                    j.jouer(d, ref reference);
                    j.viderPiecesMangees(d);
                    d.changerTour();
                    eval = MIN(d, adv, prof - 1);
                    d.annulerUnTour();
                    if (eval > max)
                    {
                        max = eval;
                    }
                }
                return max;
            }
        }

        public Coup alphaBetaPremier(Damier d)  // min correspond au minimum, max correspond au maximum.
        {
            if (coupsPossibles.Count() == 1)    //Si l'ordinateur a qu'un seul coup à jouer.
                return coupsPossibles[0];

            /*joueur adverse*/
            Joueur adv = null;
            if (this == d.blanc)
                adv = d.noir;
            else if (this == d.noir)
                adv = d.blanc;

            /*Variables locales*/
            Coup meilleurCoup = null;
            int beta = int.MinValue;
            int eval;
            Coup reference = null;
            List<Coup> coupsJouables = new List<Coup>();

            int evalMax = int.MinValue;

            /*Pour tous les coups du joueur*/
            foreach (Coup c in this.coupsPossibles)
            {
                reference = c;
                this.jouer(d, ref reference);
                this.viderPiecesMangees(d);
                d.afficheDamier();
                d.changerTour();    //change monTour du joueur et calcul ses coups possibles
                eval = ALPHA(d, adv, profondeur - 1, evalMax);
                d.annulerUnTour();
                d.afficheDamier();
                
                if (eval < evalMax) //Si l'eval est plus petite que evalMax, on passe au coup suivant.
                    continue;

                if (eval > beta)
                {
                    beta = eval;
                    evalMax = eval;
                    coupsJouables.Clear();
                    coupsJouables.Add(c);
                }
                else if (eval == beta)
                {
                    coupsJouables.Add(c);
                }
            }
            Random r = new Random();
            meilleurCoup = coupsJouables[r.Next(coupsJouables.Count())];
            return meilleurCoup;
        }

        private int ALPHA(Damier d, Joueur adv, int prof, int evalMax)   //correspond au min
        {
            Joueur j = null;
            if (adv == d.blanc)
                j = d.noir;
            else if (adv == d.noir)
                j = d.blanc;

            if (prof == 0 || d.finPartie() != 0)
                return evaluation(d, j);
            else
            {
                int alpha = int.MaxValue;
                int eval;
                Coup reference;
                foreach (Coup c in adv.coupsPossibles)
                {
                    reference = c;
                    if (adv is Humain)
                        (adv as Humain).jouerIA(d, ref reference);
                    else
                        adv.jouer(d, ref reference);
                    adv.viderPiecesMangees(d);
                    d.afficheDamier();
                    d.changerTour();    //change monTour du joueur et calcul ses coups possibles
                    eval = BETA(d, j, prof - 1, evalMax);
                    d.annulerUnTour();

                    /*Si l'eval est plus petite que evalMax, ça signifie que le coup de départ peut donner une situation moins avantageuse, donc on sort.*/
                    if (eval < evalMax)
                    {
                        alpha = eval;
                        break;
                    }
                    if (eval < alpha)
                    {
                        alpha = eval;
                    }
                }
                return alpha;
            }
        }

        private int BETA(Damier d, Joueur j, int prof, int evalMax)  //correspond au max
        {
            Joueur adv = null;
            if (j == d.blanc)
                adv = d.noir;
            else if (j == d.noir)
                adv = d.blanc;

            if (prof == 0 || d.finPartie() != 0)
                return evaluation(d, j);
            else
            {
                int beta = int.MinValue;
                int eval;
                Coup reference;
                foreach (Coup c in j.coupsPossibles)
                {
                    reference = c;
                    j.jouer(d, ref reference);
                    j.viderPiecesMangees(d);
                    d.changerTour();
                    eval = ALPHA(d, adv, prof - 1, evalMax);
                    d.annulerUnTour();

                    if (eval > beta)
                    {
                        beta = eval;
                    }
                }
                return beta;
            }
        }

        public Coup coupAleatoire()
        {
            Random r = new Random();
            int n = r.Next(this.coupsPossibles.Count());
            Console.WriteLine("\n==> " + n + " <==\n");
            return this.coupsPossibles[n];
        }
        #endregion
    }
}
